package com.ssy.lingxi.member.merchant.utils;

import com.ssy.lingxi.member.merchant.entity.MenuDO;
import com.ssy.lingxi.member.merchant.model.bo.AuthBO;
import com.ssy.lingxi.member.merchant.model.bo.ButtonBO;
import com.ssy.lingxi.member.merchant.model.bo.LoginAuthBO;
import com.ssy.lingxi.member.merchant.model.vo.basic.response.AuthTreeButtonVO;
import com.ssy.lingxi.member.merchant.model.vo.basic.response.AuthTreeMenuAndButtonVO;
import com.ssy.lingxi.member.merchant.model.vo.basic.response.AuthTreeMenuVO;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * AuthBO工具类
 * @author 万宁
 * @version 2.0.0
 * @date 2020-06-24
 */
public class AuthBOUtils {

    /**
     * 从用户权限中抽取角色权限中包含的权限
     * <p>用于用户登录接口</p>
     * @param userAuthBOList 用户权限列表
     * @param roleAuthBOList 角色权限列表
     * @param defaultUrls 如果用户没有任何权限，返回此默认的菜单列表
     * @return 用户以当前角色登录后，可访问的菜单Url（前端Web）列表
     */
    public static List<String> getAccessUrlList(List<AuthBO> userAuthBOList, List<AuthBO> roleAuthBOList, Integer loginSource, List<String> defaultUrls) {
        if(CollectionUtils.isEmpty(userAuthBOList)) {
            return new ArrayList<>(defaultUrls);
        }

        List<String> urls = userAuthBOList.stream().filter(userAuth -> userAuth.getSource().equals(loginSource) && roleAuthBOList.stream().anyMatch(roleAuth -> roleAuth.getId().equals(userAuth.getId()))).map(AuthBO::getUrl).collect(Collectors.toList());

        return CollectionUtils.isEmpty(urls) ? defaultUrls : urls;
    }

    /**
     * 从用户权限中抽取角色权限中包含的权限
     * <p>用于用户登录接口</p>
     * @param validatePass 是否审核通过
     * @param userAuthBOList 用户权限列表
     * @param roleAuthBOList 角色权限列表
     * @param defaultUrls 如果用户没有任何权限，返回此默认的菜单列表
     * @return 用户以当前角色登录后，可访问的菜单Url（前端Web）列表
     */
    public static List<LoginAuthBO> getAccessAuth(Boolean validatePass, List<AuthBO> userAuthBOList, List<AuthBO> roleAuthBOList, Integer loginSource, List<String> defaultUrls) {
        if(!validatePass || CollectionUtils.isEmpty(userAuthBOList)) {
            return defaultUrls.stream().map(LoginAuthBO::new).collect(Collectors.toList());
        }

        return userAuthBOList.stream().filter(userAuth -> userAuth.getSource().equals(loginSource) && roleAuthBOList.stream().anyMatch(roleAuth -> roleAuth.getId().equals(userAuth.getId()))).map(auth -> new LoginAuthBO(auth.getUrl(), auth.getButtons().stream().map(ButtonBO::getCode).collect(Collectors.toList()))).collect(Collectors.toList());
    }

    /**
     * 复制一个List<AuthBO>列表
     * @param authBOList 要复制的列表
     * @return 复制的结果
     */
    public static List<AuthBO> copyList(List<AuthBO> authBOList) {
        return CollectionUtils.isEmpty(authBOList) ? new ArrayList<>() : authBOList.stream().map(AuthBOUtils::copy).collect(Collectors.toList());
    }

    /**
     * 复制一个AuthBO对象
     * @param authBO 权限对象
     * @return 复制结果
     */
    public static AuthBO copy(AuthBO authBO) {
        if(authBO == null) {
            return null;
        }

        return new AuthBO(authBO.getId(), authBO.getParentId(), authBO.getCode(), authBO.getUrl(), authBO.getTitle(), authBO.getOrder(), authBO.getSource(), authBO.getAttrs(), copyButtonList(authBO.getButtons()), authBO.getStatus());
    }

    /**
     * 复制按钮
     * @param button 按钮
     * @return 复制结果
     */
    public static ButtonBO copyButton(ButtonBO button) {
        return button == null ? null : new ButtonBO(button.getButtonId(), button.getTitle(), button.getUrl(), button.getCode(), button.getAttrs());
    }

    /**
     * 复制按钮列表
     * @param buttonList 按钮列表
     * @return 复制结果
     */
    public static List<ButtonBO> copyButtonList(List<ButtonBO> buttonList) {
        return CollectionUtils.isEmpty(buttonList) ? new ArrayList<>() : buttonList.stream().map(AuthBOUtils::copyButton).collect(Collectors.toList());
    }

    /**
     * 合并两个List<AuthBO>，并且去重
     * @param first 第一个列表
     * @param second 第二个列表
     * @return 合并结果（复制）
     */
    public static List<AuthBO> mergeAndDistinct(List<AuthBO> first, List<AuthBO> second) {
        List<AuthBO> resultList = new ArrayList<>();
        resultList.addAll(first);
        resultList.addAll(second);
        return distinct(resultList);
    }

    /**
     * 合并多个List<AuthBO>，并且去重
     * @param lists List<List<AuthBO>>参数
     * @return 合并结果（复制）
     */
    public static List<AuthBO> mergeAndDistinct(List<List<AuthBO>> lists) {
        List<AuthBO> resultList = lists.stream().flatMap(Collection::stream).collect(Collectors.toList());
        return distinct(resultList);
    }

    /**
     * 合并多个List<AuthBO>，并且去重
     * @param lists Set<List<AuthBO>>参数
     * @return 合并结果（复制）
     */
    public static List<AuthBO> mergeAndDistinct(Set<List<AuthBO>> lists) {
        List<AuthBO> resultList = lists.stream().flatMap(Collection::stream).collect(Collectors.toList());
        return distinct(resultList);
    }

    /**
     * 去重
     * @param sourceList 列表
     * @return 去重结果（复制）
     */
    public static List<AuthBO> distinct(List<AuthBO> sourceList) {
        List<AuthBO> resultList = new ArrayList<>();

        sourceList.forEach(c -> {
            //添加Menu
            AuthBO menu = resultList.stream().filter(m -> m.getId().equals(c.getId())).findFirst().orElse(null);
            if(menu == null) {
                menu = new AuthBO(c.getId(), c.getParentId(), c.getCode(), c.getUrl(),c.getTitle(), c.getOrder(), c.getSource(), c.getAttrs(), new ArrayList<>(), c.getStatus());
                resultList.add(menu);
            }

            //添加button
            AuthBO finalMenu = menu;
            c.getButtons().forEach(b -> {
                ButtonBO button = finalMenu.getButtons().stream().filter(btn -> btn.getButtonId().equals(b.getButtonId())).findFirst().orElse(null);
                if (button == null) {
                    button = new ButtonBO(b.getButtonId(),b.getTitle(),b.getUrl(), b.getCode(), b.getAttrs());
                    finalMenu.getButtons().add(button);
                }
            });
        });

        return resultList;
    }

    /**
     * 在原来的权限中，移除仅包含在移除列表中，且不包含在排除列表中的权限
     * <p>用于删除权限</p>
     * @param sourceList 原来的权限
     * @param toRemoveList 移除列表
     * @param toExcludeList 排除列表
     */
    public static void removeFromSource(List<AuthBO> sourceList, List<AuthBO> toRemoveList, List<List<AuthBO>> toExcludeList) {
        List<AuthBO> removeList = copyList(toRemoveList);
        List<AuthBO> mergeList = mergeAndDistinct(toExcludeList);
        removeFromSource(removeList, mergeList);
        removeFromSource(sourceList, removeList);
    }

    /**
     * 从原来的权限列表中，移除包含在移除列表中的菜单
     * @param sourceList 原来的权限
     * @param removeList 要移除的列表
     */
    public static void removeFromSource(List<AuthBO> sourceList, List<AuthBO> removeList) {
        if(CollectionUtils.isEmpty(sourceList) || CollectionUtils.isEmpty(removeList)) {
            return;
        }

        //移除不存在的菜单
        sourceList.removeIf(s -> removeList.stream().noneMatch(r -> r.getId().equals(s.getId())));
    }

    /**
     * 删除sourceList中“不包含”在removeList中的权限
     * @param sourceList 原来的权限
     * @param removeList 更新的权限
     */
    public static void removeInSource(List<AuthBO> sourceList, List<AuthBO> removeList) {
        if(CollectionUtils.isEmpty(removeList) || CollectionUtils.isEmpty(sourceList)) {
            return;
        }

        //移除不存在的菜单
        sourceList.removeIf(s -> removeList.stream().noneMatch(r -> r.getId().equals(s.getId())));

        //移除不存在的按钮
        sourceList.forEach(source -> {
            if(!CollectionUtils.isEmpty(source.getButtons())) {
                removeList.stream().filter(item -> item.getId().equals(source.getId())).findFirst().ifPresent(authBO -> source.getButtons().removeIf(button -> authBO.getButtons().stream().anyMatch(b -> b.getButtonId().equals(button.getButtonId()))));
            }
        });
    }

    /**
     * 判断两个按钮列表是否完全相同（前提是所归属的菜单相同）
     * @param sourceList 第一个列表
     * @param destList 第二个列表
     * @return true:相同，false：不同
     */
    public static boolean buttonsEquals(List<ButtonBO> sourceList, List<ButtonBO> destList) {
        if (CollectionUtils.isEmpty(sourceList) && CollectionUtils.isEmpty(destList)) {
            return true;
        }

        if(CollectionUtils.isEmpty(sourceList) && !CollectionUtils.isEmpty(destList)) {
            return false;
        }

        if(!CollectionUtils.isEmpty(sourceList) && CollectionUtils.isEmpty(destList)) {
            return false;
        }

        boolean sourceAllInDest = sourceList.stream().allMatch(s -> destList.stream().anyMatch(d -> d.getButtonId().equals(s.getButtonId())));
        boolean destAllInSource = destList.stream().allMatch(d -> sourceList.stream().anyMatch(s -> s.getButtonId().equals(d.getButtonId())));
        return sourceAllInDest && destAllInSource;
    }

    /**
     * 判断两个列表是否不完全相同
     * @param sourceList 第一个列表
     * @param destList 第二个列表
     * @return  true:不同，false:相同
     */
    public static boolean listNotEqual(List<AuthBO> sourceList, List<AuthBO> destList) {
        if (CollectionUtils.isEmpty(sourceList) && CollectionUtils.isEmpty(destList)) {
            return false;
        }

        if(CollectionUtils.isEmpty(sourceList) && !CollectionUtils.isEmpty(destList)) {
            return true;
        }

        if(!CollectionUtils.isEmpty(sourceList) && CollectionUtils.isEmpty(destList)) {
            return true;
        }

        if(sourceList.size() != destList.size()) {
            return true;
        }

        boolean sourceAllInDes = sourceList.stream().allMatch(s -> destList.stream().anyMatch(d -> d.equals(s)));
        if(!sourceAllInDes) {
            return true;
        }

        return !destList.stream().allMatch(d -> sourceList.stream().anyMatch(s -> s.equals(d)));
    }

    public static List<AuthTreeMenuVO> transferToTree(List<AuthBO> authBOList) {
        if(CollectionUtils.isEmpty(authBOList)) {
            return new ArrayList<>();
        }

        List<AuthTreeMenuVO> resultList = new ArrayList<>();

        //找到第一级菜单
        List<AuthBO> topList = authBOList.stream().filter(t -> t.getParentId().equals(0L)).sorted(Comparator.comparingInt(AuthBO::getOrder)).collect(Collectors.toList());
        //循环、递归构建树
        topList.forEach(top -> {
            AuthTreeMenuVO topVO = new AuthTreeMenuVO(top.getId(), top.getTitle(), null);
            recurseTree(topVO, top, authBOList);
            resultList.add(topVO);
        });

        return resultList;
    }

    private static void recurseTree(AuthTreeMenuVO topVO, AuthBO top , List<AuthBO> authBOList) {
        List<AuthBO> subList = authBOList.stream().filter(s -> s.getParentId().equals(top.getId())).sorted(Comparator.comparingInt(AuthBO::getOrder)).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(subList)) {
            return;
        }

        for (AuthBO sub : subList) {
            AuthTreeMenuVO menuBO = new AuthTreeMenuVO(sub.getId(), sub.getTitle(), null);
            topVO.getChildren().add(menuBO);
            recurseTree(menuBO, sub, authBOList);
        }
    }

    public static List<AuthTreeMenuVO> transferMenuToTree(List<MenuDO> menuDOList) {
        if(CollectionUtils.isEmpty(menuDOList)) {
            return new ArrayList<>();
        }

        List<AuthTreeMenuVO> resultList = new ArrayList<>();

        List<MenuDO> topList = menuDOList.stream().filter(menuDO -> menuDO.getParentId().equals(0L)).collect(Collectors.toList());

        //循环、递归构建树
        topList.forEach(top -> {
            AuthTreeMenuVO topVO = new AuthTreeMenuVO(top.getId(), top.getTitle(), null);
            recurseMenuToTree(topVO, top, menuDOList);
            resultList.add(topVO);
        });

        return resultList;
    }

    private static void recurseMenuToTree(AuthTreeMenuVO topVO, MenuDO top , List<MenuDO> menuDOList) {
        List<MenuDO> subList = menuDOList.stream().filter(s -> s.getParentId().equals(top.getId())).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(subList)) {
            return;
        }

        for (MenuDO sub : subList) {
            AuthTreeMenuVO menuBO = new AuthTreeMenuVO(sub.getId(), sub.getTitle(), null);
            topVO.getChildren().add(menuBO);
            recurseMenuToTree(menuBO, sub, menuDOList);
        }
    }


    /**
     * 递归构建权限树形结构，用于平台后台、会员能力修改权限
     * @param authBOList 权限列表
     * @return 树形结构的权限列表
     */
    public static List<AuthTreeMenuAndButtonVO> transferToFullTree(List<AuthBO> authBOList) {
        if(CollectionUtils.isEmpty(authBOList)) {
            return new ArrayList<>();
        }

        List<AuthTreeMenuAndButtonVO> resultList = new ArrayList<>();

        //根据key是否包含“-”，找到第一级菜单
        List<AuthBO> topList = authBOList.stream().filter(t -> t.getParentId().equals(0L)).collect(Collectors.toList());
        //循环、递归构建树
        topList.forEach(top -> {
            List<AuthTreeButtonVO> buttons = top.getButtons().stream().map(b -> new AuthTreeButtonVO(b.getButtonId(), b.getTitle())).collect(Collectors.toList());
            AuthTreeMenuAndButtonVO topVO = new AuthTreeMenuAndButtonVO(top.getId(), top.getTitle(), buttons, null);
            recurseFullTree(topVO, top, authBOList);
            resultList.add(topVO);
        });

        return resultList;
    }

    /**
     * 递归构建权限树形菜单私有函数
     * @param topVO 上级菜单
     * @param top  上级权限
     * @param authBOList 权限列表
     */
    private static void recurseFullTree(AuthTreeMenuAndButtonVO topVO, AuthBO top , List<AuthBO> authBOList) {
        List<AuthBO> subList = authBOList.stream().filter(s -> s.getParentId().equals(top.getId())).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(subList)) {
            return;
        }

        for (AuthBO sub : subList) {
            List<AuthTreeButtonVO> buttons = CollectionUtils.isEmpty(sub.getButtons()) ? new ArrayList<>() : sub.getButtons().stream().map(b -> new AuthTreeButtonVO(b.getButtonId(), b.getTitle())).collect(Collectors.toList());
            AuthTreeMenuAndButtonVO menuBO = new AuthTreeMenuAndButtonVO(sub.getId(), sub.getTitle(), buttons, null);
            topVO.getChildren().add(menuBO);
            recurseFullTree(menuBO, sub, authBOList);
        }
    }

    /**
     * 从sourceList中筛选出包含在destList中的，并且source为指定登录来源的权限菜单列表，用于用户登录接口等
     * @param sourceList 被筛选的权限列表
     * @param destList   筛选依据
     * @param source     登录来源、菜单的source
     * @return 筛选结果
     */
    public static List<AuthBO> selectFromSource(List<AuthBO> sourceList, List<AuthBO> destList, Integer source) {
        if(CollectionUtils.isEmpty(destList)) {
            return sourceList.stream().filter(sourceAuth -> sourceAuth.getSource().equals(source)).collect(Collectors.toList());
        } else {
            List<AuthBO> resultList = sourceList.stream().filter(sourceAuth -> sourceAuth.getSource().equals(source) && destList.stream().anyMatch(destAuth -> destAuth.getId().equals(sourceAuth.getId()))).collect(Collectors.toList());
            resultList.forEach(sourceAuth -> destList.stream().filter(auth -> auth.getId().equals(sourceAuth.getId())).findFirst().ifPresent(destAuth -> sourceAuth.setButtons(sourceAuth.getButtons().stream().filter(button -> destAuth.getButtons().stream().anyMatch(destAuthButton -> destAuthButton.getButtonId().equals(button.getButtonId()))).collect(Collectors.toList()))));
            return resultList;
        }
    }


    /**
     * 根据菜单的key，判断菜单的层级
     * @param key 菜单Key
     * @return 菜单层级
     */
    public static Integer getKeyLevel(String key) {
        return key.split("-").length;
    }

    /**
     * 从菜单key的最后一位数，获得菜单的顺序
     * @param key 菜单Key
     * @return 菜单顺序
     */
    public static Integer getKeyOrder(String key) {
        String[] keyNum = key.split("-");
        return Integer.valueOf(keyNum[keyNum.length-1]);
    }
}
